use crate::common::list::ListHead;
use crate::common::synchronization::interface::Mutex;
use crate::mm::{buddy, buddy::virt_to_page, buddy::Page, buddy::BUDDY_PAGE_SIZE, mm::global_mem};
use crate::{kdebug, kwarn, BUG_ON};
use core::ptr;
static mut slabs: [*mut SlabHeader; SLAB_MAX_ORDER + 1] = [ptr::null_mut(); SLAB_MAX_ORDER + 1];
const SLAB_MAX_ORDER: usize = 6;
const SLAB_MIN_ORDER: usize = 0;
const SLAB_INIT_SIZE: usize = 4096;

pub struct SlabHeader {
    free_list_head: *mut SlabSlotList,
    next_slab: *mut SlabHeader,
    order: u64,
}

struct SlabSlotList {
    next_free: *mut SlabSlotList,
}

fn size_to_order(size: u64) -> u64 {
    let mut order = 0;
    let mut tmp = size;

    while tmp > 1 {
        tmp >>= 1;
        order += 1;
    }
    if size > (1 << order) {
        order += 1;
    }

    order
}
// order转换为相应的(物理页)数量
fn order_to_size(order: u64) -> u64 {
    1 << order
}

// 为给定size大小的内存块分配物理内存页
unsafe fn alloc_slab_memory(size: u64) -> *mut u8 {
    let mut p_page: *mut Page;
    let mut page: *mut ListHead<Page>;
    let mut addr: *mut u8;
    let mut order: u64;
    let mut page_num: u64;
    let mut page_addr;
    let mut i: usize;

    order = size_to_order(size / BUDDY_PAGE_SIZE as u64);

    // 调用伙伴系统来获取物理页
    match buddy::buddy_get_pages(&mut global_mem, order) {
        Some(page) => p_page = page as *mut Page,
        None => panic!("Failed to get pages"),
    }
    if p_page.is_null() {
        kwarn!("failed to alloc_slab_memory: out of memory\n");
        BUG_ON!(true);
    }
    // 将物理页转换为虚拟地址
    addr = global_mem
        .inner
        .lock(|inner| buddy::page_to_virt(&inner, &mut *p_page));

    // 计算页的数量
    page_num = order_to_size(order);
    // 将每个页的slab字段指向addr对应的slab
    // 应该就是指向这一段连续页的内存的开始
    for i in 0..page_num {
        page_addr = (addr as u64 + i * BUDDY_PAGE_SIZE);
        page = global_mem
            .inner
            .lock(|inner| virt_to_page(&inner, page_addr));
        (*page).data.slab = addr as *mut SlabHeader;
    }
    addr
}

// 初始化slab缓存
unsafe fn init_slab_cache(order: usize, size: usize) -> *mut SlabHeader {
    // 声明变量
    let mut addr: *mut u8;
    let mut slot: *mut SlabSlotList;
    let mut slab: *mut SlabHeader;
    let mut cnt: u64;
    let mut obj_size: u64;
    let mut i: usize;

    // 分配内存
    addr = alloc_slab_memory(size as u64);
    slab = (addr) as *mut SlabHeader;

    // 计算对象大小
    obj_size = order_to_size(order as u64);
    cnt = (size / obj_size as usize - 1) as u64;

    // 初始化空闲列表
    slot = (addr as usize + obj_size as usize) as *mut SlabSlotList;
    (*slab).free_list_head = slot as *mut SlabSlotList;
    (*slab).next_slab = core::ptr::null_mut();
    (*slab).order = order as u64;

    // 初始化空闲列表
    for i in 0..cnt - 1 {
        (*slot).next_free = (slot as usize + obj_size as usize) as *mut SlabSlotList;
        slot = (slot as usize + obj_size as usize) as *mut SlabSlotList;
    }
    (*slot).next_free = core::ptr::null_mut();

    // 返回slab
    slab
}

// 在给定的slab中分配内存
// slab_header:指向slab头部的指针   order:分配的内存大小
unsafe fn _alloc_in_slab_nolock(slab_header: *mut SlabHeader, order: usize) -> *mut u8 {
    // 声明变量
    let mut first_slot: *mut SlabSlotList;
    let mut next_slot;
    let mut next_slab: *mut SlabHeader;
    let mut new_slab: *mut SlabHeader;

    // 检查空闲列表
    first_slot = (*slab_header).free_list_head;
    if !first_slot.is_null() {
        next_slot = (*first_slot).next_free;
        (*slab_header).free_list_head = next_slot;
        return first_slot as *mut u8;
    }

    // 遍历所有slab
    next_slab = (*slab_header).next_slab;
    while !next_slab.is_null() {
        first_slot = (*next_slab).free_list_head;
        if !first_slot.is_null() {
            next_slot = (*first_slot).next_free;
            (*next_slab).free_list_head = next_slot as *mut SlabSlotList;
            return first_slot as *mut u8;
        }
        next_slab = (*next_slab).next_slab;
    }

    // 创建新的slab
    new_slab = init_slab_cache(order, SLAB_INIT_SIZE);
    (*new_slab).next_slab = slab_header;
    slabs[order] = new_slab;

    // 递归分配
    _alloc_in_slab_nolock(new_slab, order)
}

unsafe fn _alloc_in_slab(slab_header: *mut SlabHeader, order: usize) -> *mut u8 {
    let free_slot = _alloc_in_slab_nolock(slab_header, order);
    free_slot
}

// 初始化slab
pub fn init_slab() {
    for order in SLAB_MIN_ORDER..=SLAB_MAX_ORDER {
        unsafe { slabs[order] = init_slab_cache(order, SLAB_INIT_SIZE) };
    }
    kdebug!("mm: finish initing slab allocators\n");
}

// 在slab中分配一块大小为size的内存
fn alloc_in_slab(size: u64) -> *mut u8 {
    assert!(size <= order_to_size(SLAB_MAX_ORDER as u64));
    let order = size_to_order(size);
    let order = if order < SLAB_MIN_ORDER as u64 {
        SLAB_MIN_ORDER
    } else {
        order as usize
    };
    unsafe { _alloc_in_slab(slabs[order], order) }
}

// 释放addr对应的内存块
unsafe fn free_in_slab(addr: *mut u8) {
    global_mem.inner.lock(|pool| {
        let page = virt_to_page(&pool, addr as u64);
        assert!(!page.is_null());

        let slab: *mut SlabHeader = (*page).data.slab;
        let slot: *mut SlabSlotList = addr as *mut SlabSlotList;
        // 将该内存块加入到空闲链表中
        (*slot).next_free = (*slab).free_list_head;
        (*slab).free_list_head = slot;
    });
}
